using System.IO;
using System.Text;
using Oracle.ManagedDataAccess.Client;
using OracleInternal.I18N;

namespace OracleInternal.BinXml
{
	internal class ObxmlOutputObject
	{
		internal const int s_OutputNumericValueNotSet = -1;

		private InputOutputTypes m_outputContentType = InputOutputTypes.None;

		internal object m_OutputObject;

		internal ObxmlDecodeRequest m_Parent;

		private static int DefaultUnicodeStringSize = 16384;

		internal char[] m_UnicodeArray = new char[DefaultUnicodeStringSize];

		private bool m_UnicodeArrayInUse;

		private object m_nlsLock = new object();

		internal long WriteOffset
		{
			get;
			set;
		}

		internal bool OutputObjectOwnedByClient
		{
			get;
			set;
		}

		internal InputOutputTypes OutputType
		{
			get
			{
				return m_outputContentType;
			}
			set
			{
				if (IsOutputContentTypeValid(value))
				{
					m_outputContentType = value;
				}
			}
		}

		internal OutputEncodingTypes EncodingType
		{
			get;
			set;
		}

		internal bool IsOutputType(InputOutputTypes outputType)
		{
			return OutputType == outputType;
		}

		internal ObxmlOutputObject(ObxmlDecodeRequest parent)
		{
			m_Parent = parent;
			ResetOutputObject();
		}

		internal int ReadTextInUnicode(byte[] bytes, int offset, int bytesCount)
		{
			int charCount = 0;
			if (bytes == null || bytesCount <= 0)
			{
				return 0;
			}
			if (bytesCount < m_UnicodeArray.Length + offset - 1)
			{
				ObxmlStream.Unicode_AL16UTF16_CHARSET.ConvertBytesToChars(bytes, offset, bytesCount, m_UnicodeArray, 0, ref charCount);
			}
			else
			{
				DefaultUnicodeStringSize = bytesCount + 2;
				m_UnicodeArray = new char[DefaultUnicodeStringSize];
				ObxmlStream.Unicode_AL16UTF16_CHARSET.ConvertBytesToChars(bytes, offset, bytesCount, m_UnicodeArray, 0, ref charCount);
			}
			return charCount;
		}

		internal string ReadTextInUnicode_old(byte[] bytes, int offset, int bytesCount)
		{
			if (bytes == null || bytesCount <= 0)
			{
				return null;
			}
			if (!m_UnicodeArrayInUse)
			{
				string text = string.Empty;
				lock (m_nlsLock)
				{
					if (!m_UnicodeArrayInUse)
					{
						m_UnicodeArrayInUse = true;
						if (bytesCount < m_UnicodeArray.Length + offset - 1)
						{
							text = ObxmlStream.Unicode_AL16UTF16_CHARSET.ConvertBytesToString(bytes, offset, bytesCount, m_UnicodeArray);
						}
						m_UnicodeArrayInUse = false;
					}
				}
				if (!string.IsNullOrEmpty(text))
				{
					return text;
				}
			}
			return ObxmlStream.Unicode_AL16UTF16_CHARSET.ConvertBytesToString(bytes, offset, bytesCount);
		}

		internal void SetObxmlOutputObject(byte[] textBuffer, bool outputOwnedByClient)
		{
			OutputType = InputOutputTypes.ByteArray;
			m_OutputObject = textBuffer;
			OutputObjectOwnedByClient = outputOwnedByClient;
		}

		internal void SetObxmlOutputObject(ref StringBuilder utf16OutputBuilder, bool outputOwnedByClient, long inputEstimate = 0L)
		{
			OutputType = InputOutputTypes.StringBuilder;
			if (m_OutputObject == null && utf16OutputBuilder == null)
			{
				m_OutputObject = (utf16OutputBuilder = new StringBuilder(ObxmlDecodeContext.GetOutputLengthEstimate((int)inputEstimate)));
			}
			else if (utf16OutputBuilder != null)
			{
				m_OutputObject = utf16OutputBuilder;
			}
			else
			{
				utf16OutputBuilder = (StringBuilder)m_OutputObject;
			}
			OutputObjectOwnedByClient = outputOwnedByClient;
			EncodingType = OutputEncodingTypes.Utf16;
		}

		internal void SetObxmlOutputObject(byte[] textBuffer, long offset)
		{
			OutputType = InputOutputTypes.ByteArray;
			m_OutputObject = textBuffer;
			WriteOffset = offset;
		}

		internal void SetObxmlOutputObject(FileStream outputFileStream)
		{
			OutputType = InputOutputTypes.FileStream;
			m_OutputObject = outputFileStream;
		}

		internal long WriteTextOutput(StringBuilder sbText, int readOffset)
		{
			long num = m_Parent.m_CountRemaining / 2;
			if (num > sbText.Length - readOffset)
			{
				num = sbText.Length - readOffset;
			}
			long num2 = num * 2;
			if (IsOutputType(InputOutputTypes.StringBuilder))
			{
				StringBuilder stringBuilder = (StringBuilder)m_OutputObject;
				if (readOffset == 0 && num == sbText.Length)
				{
					stringBuilder.Append(sbText.ToString());
				}
				else
				{
					stringBuilder.Append(sbText.ToString(readOffset, (int)num));
				}
				m_Parent.m_CountRemaining -= num2;
			}
			return num;
		}

		internal long WriteTextOutput(string text)
		{
			int num = 0;
			if (IsOutputType(InputOutputTypes.StringBuilder))
			{
				((StringBuilder)m_OutputObject).Append(text);
				num = text.Length;
				m_Parent.m_CountRemaining -= num * 2;
				return num;
			}
			throw new OracleException(ResourceStringConstants.XML_TYPE_BINARY_INTERNAL_ERROR, string.Empty, string.Empty, ObxmlDecodeResponse.GetErrorMessage(ObxmlErrorTypes.InvalidArguments));
		}

		internal long WriteTextOutput(ObxmlStream input, int lengthToCopyFromInput, Conv.DelegateReplaceCharsInSB replaceDelegate = null)
		{
			int num = 0;
			if (IsOutputType(InputOutputTypes.StringBuilder))
			{
				StringBuilder sb = (StringBuilder)m_OutputObject;
				num = input.ReadUtf8StringSB((ulong)lengthToCopyFromInput, sb, replaceDelegate);
				m_Parent.m_CountRemaining -= num * 2;
				return num;
			}
			throw new OracleException(ResourceStringConstants.XML_TYPE_BINARY_INTERNAL_ERROR, string.Empty, string.Empty, ObxmlDecodeResponse.GetErrorMessage(ObxmlErrorTypes.InvalidArguments));
		}

		internal long WriteTextOutput(string text, int readOffset)
		{
			long num = m_Parent.m_CountRemaining / 2;
			if (num > text.Length - readOffset)
			{
				num = text.Length - readOffset;
			}
			long num2 = num * 2;
			if (IsOutputType(InputOutputTypes.StringBuilder))
			{
				StringBuilder stringBuilder = (StringBuilder)m_OutputObject;
				if (readOffset == 0 && num == text.Length)
				{
					stringBuilder.Append(text);
				}
				else
				{
					stringBuilder.Append(text, readOffset, (int)num);
				}
				m_Parent.m_CountRemaining -= num2;
			}
			return num;
		}

		internal long WriteTextOutput(char[] text, int readOffset, int length = -1)
		{
			long num = m_Parent.m_CountRemaining / 2;
			if (length == -1)
			{
				length = text.Length;
			}
			if (num > length - readOffset)
			{
				num = length - readOffset;
			}
			if (IsOutputType(InputOutputTypes.StringBuilder))
			{
				((StringBuilder)m_OutputObject).Append(text, readOffset, (int)num);
				m_Parent.m_CountRemaining -= 2 * num;
				return num;
			}
			throw new OracleException(ResourceStringConstants.XML_TYPE_BINARY_INTERNAL_ERROR, string.Empty, string.Empty, ObxmlDecodeResponse.GetErrorMessage(ObxmlErrorTypes.RequestOutputInvalid));
		}

		internal long WriteTextOutput(byte[] text, int readOffset)
		{
			long num = m_Parent.m_CountRemaining;
			if (num > text.Length - readOffset)
			{
				num = text.Length - readOffset;
			}
			if (IsOutputType(InputOutputTypes.StringBuilder))
			{
				StringBuilder stringBuilder = (StringBuilder)m_OutputObject;
				if (EncodingType == OutputEncodingTypes.Utf16)
				{
					int num2 = 0;
					stringBuilder.Append(charCount: (readOffset != 0) ? ReadTextInUnicode(text, readOffset, (int)num) : ReadTextInUnicode(text, 0, text.Length), value: m_UnicodeArray, startIndex: 0);
				}
				else
				{
					byte[] array = ObxmlStream.Utf8_AL32UTF8_CHARSET.ConvertBytesToUTF16(text, readOffset, (int)num);
					int charsConverted = 0;
					ObxmlStream.Unicode_AL16UTF16_CHARSET.ConvertBytesToStringSB(stringBuilder, array, 0, array.Length, ref charsConverted);
					num = charsConverted * 2;
				}
				m_Parent.m_CountRemaining -= num;
			}
			return num;
		}

		internal bool IsOutputContentTypeValid(InputOutputTypes contentType)
		{
			if (InputOutputTypes.ByteArray == contentType || InputOutputTypes.StringBuilder == contentType || InputOutputTypes.BinaryXmlUri == contentType || contentType == InputOutputTypes.XmlFilePath)
			{
				return true;
			}
			return false;
		}

		internal bool IsOutputValid()
		{
			return OutputType != InputOutputTypes.None;
		}

		internal bool ResetOutputObject()
		{
			WriteOffset = 0L;
			OutputType = InputOutputTypes.None;
			EncodingType = OutputEncodingTypes.None;
			if (!OutputObjectOwnedByClient)
			{
				StringBuilder stringBuilder = (StringBuilder)m_OutputObject;
				if (stringBuilder != null)
				{
					stringBuilder.Length = 0;
					int outputResetLengthEstimate = ObxmlDecodeContext.GetOutputResetLengthEstimate();
					if (outputResetLengthEstimate < stringBuilder.Capacity)
					{
						stringBuilder.Capacity = outputResetLengthEstimate;
					}
				}
			}
			OutputObjectOwnedByClient = false;
			return true;
		}
	}
}
